home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Software Vault: The Gold Collection
/
Software Vault - The Gold Collection (American Databankers) (1993).ISO
/
cdr23
/
chansw53.zip
/
CHAINSAW.CPP
< prev
next >
Wrap
C/C++ Source or Header
|
1993-05-24
|
22KB
|
702 lines
// FILE: CHAINSAW.CPP
// MODULE: MAIN
// PROGRAM: CHAINSAW
// Public Domain, 5/24/93, Ted Davis
//////////////////////////////////////////////////////////////////////////////
// CHAINSAW is a command line driven directory pruner. It does not stop for
// user confirmation and can, therefore, readily be used from batch files.
// It provides password protection to prevent casual prowlers from
// using it to accidentally trash a disk or directory. The complete path,
// including drive, to the directory node at which the pruning is to occur
// is passed in the command tail as the starting directory.
// Switches are provided to allow the program to delete the files in that
// directory, and to remove that directory, as well as all the subdirectories
// and their contents. Without that switch, only the subdirectories and
// their contents will be removed.
// Another switch allows operation on the root directory. Other switches
// control which messages are written and their destinations, operation on
// network drives, changing the defaults, and changing the password.
//
// Version history:
// α.1 - the first working version, without most of the switches of
// later versions. (mostly working, that is.)
// α.2 - debugged version of α.1
//
// ß.1 - /f and /r switches added; comments solicited; default directory
// bug discovered.
//
// ß.2 - comments incorporated, message control switches added; default
// directory on the branch to be pruned code fixed to correctly
// back up the tree toward the root as directories are removed.
// First outside testing (no problems found).
//
// ß.3 - ^Break code added to allow the user to abort the program; password
// changing code added and simple test of password against a #define
// changed to compare against an encrypted password stored in the
// EXE file.
//
// ß.4 - F and f switches separated to allow removal of starting directory.
// fix read-only, etc code, clean up several places; added password
// bypass feature.
//
// ß.5 _ Added summary at end of program, default changing, rework switches,
// and added network drive lockout. This was a fairly extensive
// rework of the head end of the program. Switches can now be in
// /B /D /E, /B/D/E, /BDE (or combinations, or using '-') format and
// commas, semicolons, and tabs are allowed.
//
// ß.5.1 - Fixed network drive lockout to compensate for incorrect
// documentation of INT 21, Function 0x4409.
//
// ß.5.2 - Bug fix (handling of empty starting directories).
//
// ß.5.3 - Bug fix (reinstalling a password after removal of same).
//
// 5.3 - Release Version, no function changes from the beta version.
//
// NOTE: when compiling this, you may get a warning that FileHandle is unused-
// this warning is untrue, ignore it.
//
#include "chainsaw.hpp"
int cbreak;
int DirList = no;
int ErrorLevel = no;
int FileList = no;
int IsRoot = no;
int KillBaseFilesOK = no;
int NetworkDriveOK = no;
int PasswordCheck = no;
int PrintEnable = yes;
int ProtectedOK = no;
int RemoveStartDir = no;
int RootOK = no;
int SummaryFlag = yes;
char Bad_DriveOrDir[] = "\
\nThe given drive or directory does not exist or there is a problem\n\
using it (door open, network drive but no /n switch, etc)... \n\
CHAINSAW aborted.\n";
char Bad_NewPassword[] = "\nUnable to change password.\n\
...SEE: CHAINSAW.DOC ...CHAINSAW aborted\n";
char Bad_NewSwitches[] = "\nUnable to change default switches.\n\
...SEE: CHAINSAW.DOC ...CHAINSAW aborted\n";
char Bad_Password[] = "\nPassword incorrect or file read error\
... CHAINSAW aborted\n";
char Barf_Message[] = "\
\n The calling syntax for CHAINSAW was incorrect...\n\
If you do not FULLY understand the syntax and what the program does\n\
you should not attempt to use it. This program\n\
DESTROYS ENTIRE BRANCHES OF THE DIRECTORY TREE.\n\
If used incorrectly, it can do untold damage.\n";
char Copyright_Message[] = "\n CHAINSAW Version xxxxx is FREEWARE, \
Public Domain, 5/24/93, Ted Davis\n";
char DummyMessage[256] = "";
char GoodNewPasswordMsg[] = "\nCHAINSAW...New password installed.\n";
char Help_Message[] = "\
CHAINSAW xxxxx deletes, without prompts, entire branches of a directory tree.\n\
\n\
Syntax: CHAINSAW {path to base directory} {password} {switches} \n\
or CHAINSAW {modification switch} {password}\n\
(The drive letter is required, trailing '\\' is optional. Switches may be\n\
in /bde, /b/d/e, /b /d /e, /b,/d,/e, -bde, -b-d-e, -b -d -e, etc. format, so\n\
long as the first one immediately follows a '/' or '-'.)\n\
\n\ There are too many switches to define all of them here, but...\n\
/A restores default switches, /a changes them; b,B,d,D,e,E,l,L,m,M,o,O,s,S,\n\
w,and W control the way the program writes its messages (and what messages it\n\
writes). For the rest of the listed switches, lower case enables and upper\n\
case disables the feature:\n\
f,F - file deletion in the base directory; g,G - base directory removal;\n\
n,N - operation on network drives; p,P - deletion of read-only, hidden and\n\
system files; r,R - operation on the root directory; \n\
See CHAINSAW.DOC for details.\n";
char InitialDirectory[80] = "";
char Memory_Error[] = "\n Memory allocation ERROR, CHAINSAW aborted\n";
char NewSwitchesMessage[] = "\nNew default switches installed.\n";
char NullMessage[] = "";
char OldSwitchesMessage[] = "\nOriginal default switches reinstalled.\n";
char Root_NotAllowed[] = "\nUse of root directory is not enabled... \
CHAINSAW aborted\n";
char Wrong_Version[] = "\nDOS 3.0 or later is required, \
CHAINSAW aborted.\n";
Message_ BadDriveOrDir;
Message_ BadNewPassword;
Message_ BadNewSwitches;
Message_ BadPassword;
Message_ BarfMessage;
Message_ CopyrightMessage;
Message_ DirFailed;
Message_ DirOK;
Message_ FileFailed;
Message_ FileOK;
Message_ GoodNewPassword;
Message_ HelpMessage;
Message_ MemoryError;
Message_ OldSwitches;
Message_ NewSwitches;
Message_ RootNotAllowed;
Message_ Summary;
Message_ WrongVersion;
SumData SummaryData;
void main(int argc, char *argv[])
{
int LastChar = 0;
int FileHandle;
int i, j, ChangePasswordFlag = no;
int SwitchFlag = no;
int AbortFlag = no;
char *argv1;
char *SwitchPtr;
dirkiller *dk;
char DefaultSwitches[32] = "";
char SwitchString[64] = "";
char NewPasswordString[password_len] = "";
char PasswordString[password_len] = "";
cbreak = getcbrk(); // Saves current value of break setting.
setcbrk(1); // Enables maximum ^Break testing.
ctrlbrk(CtrlBreakRoutine); // Sets up ^Break testing
// Set up message structures with default information.
BadDriveOrDir.Type = STDERR;
BadDriveOrDir.Message = Bad_DriveOrDir;
BadNewPassword.Type = STDERR;
BadNewPassword.Message = Bad_NewPassword;
BadNewSwitches.Type = STDERR;
BadNewSwitches.Message = Bad_NewSwitches;
BadPassword.Type = STDERR;
BadPassword.Message = Bad_Password;
BarfMessage.Type = STDERR;
BarfMessage.Message = Barf_Message;
CopyrightMessage.Type = STDOUT;
CopyrightMessage.Message = Copyright_Message;
// Insert the version number into the message string.
InsertVersion(Copyright_Message + 19);
DirFailed.Type = STDOUT;
DirFailed.Message = NullMessage;
DirOK.Type = NoPrint;
DirOK.Message = NullMessage;
FileFailed.Type = STDOUT;
FileFailed.Message = NullMessage;
FileOK.Type = NoPrint;
FileOK.Message = NullMessage;
GoodNewPassword.Type = STDOUT;
GoodNewPassword.Message = GoodNewPasswordMsg;
HelpMessage.Type = STDOUT;
HelpMessage.Message = Help_Message;
// Insert the version number into the message string.
InsertVersion(Help_Message + 9);
MemoryError.Type = STDERR;
MemoryError.Message = Memory_Error;
NewSwitches.Type = STDOUT;
NewSwitches.Message = NewSwitchesMessage;
OldSwitches.Type = STDOUT;
OldSwitches.Message = OldSwitchesMessage;
RootNotAllowed.Type = STDERR;
RootNotAllowed.Message = Root_NotAllowed;
Summary.Type = STDOUT;
Summary.Message = NullMessage;
WrongVersion.Type = STDERR;
WrongVersion.Message = Wrong_Version;
// Although some of the above is duplicated during switch scan, I put it
// all here too so that the messages will be initialized if needed before
// they are reinitialized, e.g., for the immediately following.
// Test for DOS 3.0 or later.
if(_osmajor < 3)
{
Output(WrongVersion);
setcbrk(cbreak); exit(1);
}
// Check password and get default switches from the .EXE file.
if((UserPassword(argv[2], argv[0], DefaultSwitches, PasswordString)) == no)
{
AbortFlag = yes;
}
// Start building a string of switches by copying the defaults to
// the head of the string. If the user supplied conflicting switches,
// they will follow and, therefore, override the defaults.
strcpy(SwitchString, DefaultSwitches);
// Add user switches to the string.
// First look for the first one that is a switch;
for(i = 1; i < argc; i++)
{
if((argv[i][0] == '/') || (argv[i][0] == '-')) {break;}
}
// then, using the same 'i', add everything else to the switch string.
for( ; i < argc; i++)
{
strcat(SwitchString, argv[i]);
// Fixup for the 'z' switch: spaces the switches (for 'z' it separates
// the switch and new password from the old password).
strcat(SwitchString, " ");
}
SwitchPtr = SwitchString;
// Scan the argument list for switches.
// Strip characters until a switch marker is found (to skip over
// the password, if any, and any spaces).
while(!SwitchFlag && SwitchPtr[0])
{
switch(SwitchPtr[0])
{
// '/' and '-' are equally acceptable as switch markers.
case '/':
case '-':
SwitchFlag = yes;
}
SwitchPtr++;
}
// Note that SwitchFlag is yes if there is a switch marker.
while(SwitchPtr[0] && !ChangePasswordFlag)
{
// Strip off switch markers and delimiters.
switch(SwitchPtr[0])
{
case '/':
case '-':
if((!SwitchPtr[1]) || (SwitchPtr[1] == ' '))
{
// If there is a marker but no switch.
SwitchFlag = no;
}
break;
case ' ':
case ',':
case ';':
case '\t':
case '"':
case '\'':
break;
case '?':
case 'h':
case 'H':
// Help message request.
// Display the copyright message.
Output(CopyrightMessage);
// Display the help message.
Output(HelpMessage);
setcbrk(cbreak); exit(4);
// If the user needs help, the program should not run.
break;
case 'a':
// Change the default switch settings.
SwitchPtr++;
for(i = 0; ((SwitchPtr[0] != ' ') && (SwitchPtr[0] != '\0')\
&& (i < 32)); i++)
{
DefaultSwitches[i] = SwitchPtr[0];
SwitchPtr++;
}
// Null terminate the string.
DefaultSwitches[i] = '\0';
if(ChangePassword(PasswordString, PasswordString,\
DefaultSwitches))
{
Output(BadNewSwitches);
}
else
{
Output(NewSwitches);
}
setcbrk(cbreak);
exit(5);
break;
case 'A':
// Restore default switch settings to initial settings.
strcpy(DefaultSwitches, DefaultSwitchString);
if(ChangePassword(PasswordString, PasswordString,\
DefaultSwitches))
{
Output(BadNewSwitches);
}
else
{
Output(OldSwitches);
}
setcbrk(cbreak);
exit(5);
break;
case 'b':
BadDriveOrDir.Type = STDOUT;
BadNewPassword.Type = STDOUT;
BadNewSwitches.Type = STDOUT;
BadPassword.Type = STDOUT;
BarfMessage.Type = STDOUT;
CopyrightMessage.Type = STDOUT;
DirFailed.Type = STDOUT;
DirOK.Type = STDOUT;
FileFailed.Type = STDOUT;
FileOK.Type = STDOUT;
GoodNewPassword.Type = STDOUT;
HelpMessage.Type = STDOUT;
MemoryError.Type = STDOUT;
NewSwitches.Type = STDOUT;
OldSwitches.Type = STDOUT;
RootNotAllowed.Type = STDOUT;
Summary.Type = STDOUT;
WrongVersion.Type = STDOUT;
break;
case 'd':
// Enable dir only listing to STDERR.
DirOK.Type = STDERR;
break;
case 'D':
// Enable dir only listing to STDOUT
DirOK.Type = STDOUT;
break;
case 'e':
BadDriveOrDir.Type = STDERR;
BadNewPassword.Type = STDERR;
BadNewSwitches.Type = STDERR;
BadPassword.Type = STDERR;
BarfMessage.Type = STDERR;
CopyrightMessage.Type = STDERR;
DirFailed.Type = STDERR;
DirOK.Type = STDERR;
FileFailed.Type = STDERR;
FileOK.Type = STDERR;
GoodNewPassword.Type = STDERR;
HelpMessage.Type = STDERR;
MemoryError.Type = STDERR;
NewSwitches.Type = STDERR;
OldSwitches.Type = STDERR;
RootNotAllowed.Type = STDERR;
Summary.Type = STDERR;
WrongVersion.Type = STDERR;
break;
case 'B': // Default.
case 'E':
BadDriveOrDir.Type = STDERR;
BadNewPassword.Type = STDERR;
BadNewSwitches.Type = STDERR;
BadPassword.Type = STDERR;
BarfMessage.Type = STDERR;
CopyrightMessage.Type = STDOUT;
DirFailed.Type = STDOUT;
DirOK.Type = NoPrint;
FileFailed.Type = STDOUT;
FileOK.Type = NoPrint;
GoodNewPassword.Type = STDOUT;
HelpMessage.Type = STDOUT;
MemoryError.Type = STDERR;
NewSwitches.Type = STDOUT;
OldSwitches.Type = STDOUT;
RootNotAllowed.Type = STDERR;
Summary.Type = STDOUT;
WrongVersion.Type = STDERR;
break;
case 'f':
// Enable file deletion for the starting directory.
KillBaseFilesOK = yes;
break;
case 'F': // Default (implied).
// Disable file deletion for the starting directory.
// Default.
KillBaseFilesOK = no;
break;
case 'g':
// Enable file deletion for the starting directory.
KillBaseFilesOK = yes;
// Enable removal of starting directory.
RemoveStartDir = yes;
break;
case 'G': // Default.
// Disable file deletion for the starting directory.
KillBaseFilesOK = no;
// Disable removal of starting directory.
RemoveStartDir = no;
break;
case 'l':
// Enable file listing to STDERR.
FileOK.Type = STDERR;
break;
case 'L':
// Enable file listing to STDOUT.
FileOK.Type = STDOUT;
break;
case 'm':
BadDriveOrDir.Type = STDERR;
BadNewPassword.Type = STDERR;
BadNewSwitches.Type = STDERR;
BadPassword.Type = STDERR;
BarfMessage.Type = STDERR;
DirFailed.Type = STDERR;
FileFailed.Type = STDERR;
MemoryError.Type = STDERR;
RootNotAllowed.Type = STDERR;
WrongVersion.Type = STDERR;
break;
case 'M':
case 'O':
BadDriveOrDir.Type = STDERR;
BadNewPassword.Type = STDERR;
BadNewSwitches.Type = STDERR;
BadPassword.Type = STDERR;
BarfMessage.Type = STDERR;
DirFailed.Type = STDOUT;
FileFailed.Type = STDOUT;
MemoryError.Type = STDERR;
RootNotAllowed.Type = STDERR;
WrongVersion.Type = STDERR;
break;
case 'n':
// Enable use on network drives.
NetworkDriveOK = yes;
break;
case 'N':
// Disable use on network drives. // Default.
NetworkDriveOK = no;
break;
case 'o':
BadDriveOrDir.Type = STDOUT;
BadNewPassword.Type = STDOUT;
BadNewSwitches.Type = STDOUT;
BadPassword.Type = STDOUT;
BarfMessage.Type = STDOUT;
DirFailed.Type = STDOUT;
FileFailed.Type = STDOUT;
MemoryError.Type = STDOUT;
RootNotAllowed.Type = STDOUT;
WrongVersion.Type = STDOUT;
break;
case 'p':
// Allow deletion of protected files.
ProtectedOK = yes;
break;
case 'P':
// Disallow deletion of protected files. // Default.
ProtectedOK = no;
break;
case 'r':
// Allow use from root directory.
RootOK = yes;
break;
case 'R':
// Disallow use from root directory. // Default.
RootOK = no;
break;
case 's':
// Enable summary. // Default.
SummaryFlag = yes;
break;
case 'S':
// Disable summary.
SummaryFlag = no;
break;
case 'w': // Default.
// Enable all messages.
PrintEnable = yes;
break;
case 'W':
// Disable all messages.
PrintEnable = no;
break;
case 'x':
FileFailed.Type = NoPrint;
break;
case 'X':
DirFailed.Type = NoPrint;
break;
case 'z':
// New password.
// If the program is invoked from the command line, it is
// desirable to remove the password from view as soon as
// possible: the lower case version of this switch does that
// by clearing the screen. The upper case version is for use
// from batch files or other situations in which it is not
// desirable to clear the screen. The lower case version
// falls through to the main switch code.
clrscr();
case 'Z':
ChangePasswordFlag = i;
// Above line causes the scan loop to terminate.
// Save the new password by copying it, character by character,
// to a string variable. Note that the new password MUST be
// either space terminated or the last thing in the string.
SwitchPtr++;
for(i = 0; i < 16; i++)
{
if((SwitchPtr[0] == ' ')||(SwitchPtr[0] == '\0')){break;}
NewPasswordString[i] = SwitchPtr[0];
SwitchPtr++;
}
// Null terminate the string.
NewPasswordString[i] = '\0';
// Note that SwitchString now starts with the last character of
// the password, and will be incremented by the last instruction
// in the scanning loop.
break;
default:
SwitchFlag = no;
break;
}
// Go to the next character.
SwitchPtr++;
// Check for bogus or missing switches.
if(!SwitchFlag)
{
Barf();
}
}
// Display the copyright message.
Output(CopyrightMessage);
// The actual abort for a bad password must follow the switch code since
// the help switch does not need one. The test must precede the switch
// code in order to read the default switches.
if(AbortFlag == yes)
{
Output(BadPassword);
setcbrk(cbreak); exit(1);
}
if(!ChangePasswordFlag)
{
// Convert the path to upper case.
argv1 = strupr(argv[1]);
// Convert '/' to '\'
i = 0;
while(argv[1][++i] != '\0') {if(argv[1][i] == '/'){argv[1][i] = '\\';}}
// ++i can be used since the first character cannot be '\'.
// Test the command tail arguments for syntax and completeness.
// This is placed after the switches since /? and /z are cases in which
// these tests would fail, even though the syntax is correct.
if(argc < 2) {Barf();} // There must be at least one argument (plus the
// program's own name as argv[0]).
if(argv1[1] != ':') // The path must begin with a drive (such as C:).
{
Barf();
}
// Strip off any trailing backslash.
// strlen() doesn't work here; it returns 0.
while(argv1[LastChar] != 0) {LastChar++;}
if(argv1[--LastChar] == '\\') {argv1[LastChar] = '\0';}
// Test for network drive, if locked out (or nonexistent drive).
if((!NetworkDriveOK) && (NetworkDriveQuery(toupper(argv[1][0])) != 0))
{
Output(BadDriveOrDir);
setcbrk(cbreak); exit(1);
}
// Test for the existence of the drive and directory by atempting to
// creat a file in it.
// creattemp() requires a trailing backslash, so add one first.
strcpy(InitialDirectory, argv1);
strcat(InitialDirectory, "\\");
// Attempt to creat an unique file in the directory.
if((FileHandle = creattemp(InitialDirectory, 0)) == no)
{
Output(BadDriveOrDir);
setcbrk(cbreak); exit(1);
}
else
{
// Get rid of the temp file in case base directory file deletion
// is not enabled.
unlink(InitialDirectory);
}
// It is necessary to restore the initial directory because creattemp()
// changed it to the complete filespec of the temporary file.
strcpy(InitialDirectory, argv1);
strcat(InitialDirectory, "\\");
}
if(ChangePasswordFlag)
{
if((ChangePassword(NewPasswordString, argv[0], DefaultSwitches))== no)
{
Output(BadNewPassword);
}
else
{
Output(GoodNewPassword);
}
setcbrk(cbreak);
exit(5);
}
// Deal with the root directory case.
// Check for root directory as the starting path.
if((LastChar == 2) || ((LastChar == 3) && (argv1[3] == '\\')))
// The root is either three characters, or four with the last
// being a backslash.
{
IsRoot = yes;
if(!RootOK)
{
// If the starting directory is the root and the /r stwich
// was not used, abort.
Output(RootNotAllowed);
setcbrk(cbreak); exit(1);
}
}
// Initialize the summary data structure.
SummaryData.Files = SummaryData.Dirs = 0;
// The following line is the main business of the program: it creates
// the first instance of the object dirkiller, the constructor for which
// does the real work.
if((dk=new dirkiller(InitialDirectory,KillBaseFilesOK,RemoveStartDir))==no)
{
Output(MemoryError);
setcbrk(cbreak); exit(1);
}
else
{
delete dk; // Free the memory allocated by "new"
}
if(SummaryFlag) {PrintSummary(SummaryData);}
setcbrk(cbreak); exit(ErrorLevel);
}